iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0
JavaScript

Signal API in Angular系列 第 5

Day 05 - Observable 與 Signal 的互通性 第 1 部分 - toSignal

  • 分享至 

  • xImage
  •  

本文至此已介紹了Signal的建立(create)、更新 (update)、擷取 (retrieve) 和衍生 (derivation)。如果我有一些舊的程式碼可以產生Observables,我可以將它們轉換為Signal嗎? RxJS的互通性可以在 @angular/core/rxjs-interop套件中使用toSignaltoObservable實現。今天我想介紹toSignal,然後明天介紹toObservable

toSignal的一些例子:

  • 服務向元件返回一個 Observable 來進行 HTTP 請求。然後,元件呼叫 toSignal 將 Observable 轉換為信號並呈現於 HTML 模板中。
  • 儲存狀態

將Observable轉換為Signal的 HTTP 請求

在以下範例中,我從API擷取所有貼文,應用toSignalObservable轉換為Signal,並顯示結果於 HTML範本中。好處是不必匯入async pipe來解析(resolve)範本中的Observable

// 建立一個Post類型來保存範本的實例。
type Post = {
  id: number;
  userId: number;
  body: string;
  title: string;
}

export class App {
  http = inject(HttpClient);
  posts = toSignal(
    this.http.get<Post[]>('https://jsonplaceholder.typicode.com/posts'),
    { initialValue: [] as Post }
  );
}

App組件中,我發出一個POST請求來擷取Post[]Observable。然後將Observable傳遞給 toSignal以轉換為Post[]signaltoSignal的第二個選項將signal初始化為空陣列,直到有新的結果到達。

@for (post of posts(); track post.id) {
  <p><span>編號 (Id): </span>{{ post.id }}</p>
  <p><span>標題 (Title): </span>{{ post.title }}</p>
  <p><span>內容 (Body): </span>{{ post.body }}</p>
  <hr />
}

@for迭代posts signal,顯示貼文的編號 (id)、標題 (title) 和內容 (body)。

https://ithelp.ithome.com.tw/upload/images/20240814/20168314kyNnZLfukG.png

使用RxJS和toSignal建立一個簡單的計數器

這是一個使用BehaviorSubjectscan運算子追蹤目前計數器的舊程式碼。

之前:

deltaSub = new BehaviorSubhect(0);
finalValue$ = deltaSub.pipe(
  scan((acc, v) => Math.min(0, Math.min(acc + v, 100)), 0)
);

Imports: [AsyncPipe], 
<div>
  <p>目前值 (Current Value): {{ finalValue$ | async }}</p>
  <div>
    <button (click)="deltaSub.next(-100)">|<<</button>
    <button (click)="deltaSub.next(-10)">-10</button>
    <button (click)="deltaSub.next(-5)">-5</button>
    <button (click)="deltaSub.next(5)">+5</button>
    <button (click)="deltaSub.next(10)">10</button>
    <button (click)="deltaSub.next(100)">>>|</button>
  </div>
</div>

toSignal應用於建立finalValuesignal並從imports陣列和範本中移除async pipe。

之後:

deltaSub = new BehaviorSubhect(0);
#finalValue$ = this.deltaSub.pipe(
   scan((acc, v) => Math.max(0, Math.min(acc + v, 100)), 0)
);
finalValue = toSignal(this.#finalValue$, { initialValue: 0 });
<div>
  <p>目前值 (Current Value): {{ finalValue() }}</p>
  <div>
    <button (click)="deltaSub.next(-100)">|<<</button>
    <button (click)="deltaSub.next(-10)">-10</button>
    <button (click)="deltaSub.next(-5)">-5</button>
    <button (click)="deltaSub.next(5)">+5</button>
    <button (click)="deltaSub.next(10)">10</button>
    <button 
  </div>
</div>

RxJSSignal版本皆可達成相同的結果。

這結束了鐵人賽的第5天。

參考:
toSignal HTTP Request Demo: https://stackblitz.com/edit/stackblitz-starters-zdbab6?file=src%2Fmain.ts
toSignal Simple Counter: https://stackblitz.com/edit/stackblitz-starters-vr3z66?file=src%2Fmain.ts


上一篇
Day 04 - Signal的技巧與陷阱
下一篇
Day 06 - RxJS 與 Signal 互通性 第 2 部分 - toObservable
系列文
Signal API in Angular39
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
mattchen0512
iT邦新手 5 級 ‧ 2024-08-26 11:36:44

一些型別上錯誤
{ initialValue: [] as Post } 應該是 { initialValue: [] as Post[] }

deltaSub = new BehaviorSubhect(0) => deltaSub = new BehaviorSubject(0);

thank you. Will fix

我要留言

立即登入留言